﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using NGuice.Inject.Internal.Util;
using NGuice.Inject.Internal;
using System.Reflection;

namespace NGuice.Inject
{
    /// <summary>
    /// C#无法使用泛型通配符，使用标记接口解决此类问题
    /// </summary>
    public interface Key
    {
        Type TypeLiteral
        {
            get;
        }

        Type AttributeType
        {
            get;
        }

        Attribute Attribute
        {
            get;
        }
    }


    public interface AttributeStrategy
    {
        Attribute Attribute
        {
            get;
        }

        Type AttributeType
        {
            get;
        }

        bool HasAttributes
        {
            get;
        }

        AttributeStrategy WithoutAttributes
        {
            get;
        }
    }

    /// <summary>
    /// 绑定关键字key由一个注入类型和一个可选的元特性组成。他在注入点上匹配类型和元特性。例如：
    /// <code>Key.Get(typeof(Service), typeof(Transactional))</code>将匹配:
    /// <code>
    /// [Inject]
    /// public void SetService([Transactional] Service service) {....}
    /// </code>
    /// Key也支持泛型参数，在key中原始类型（例如char等）与相应的打包类型（Character）没什么不同。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Key<T> : Key
    {
        private readonly AttributeStrategy attributeStrategy;

        private readonly Type typeLiteral;//T参数化类型的类型

        public Type TypeLiteral
        {
            get { return typeLiteral; }
        }

        private readonly int hashCode;

        /// <summary>
        /// 构造一个新的key，并传入元特性。客户端可以这样构造key：
        /// <code>
        /// new Key<Foo>(typeof(Bar));
        /// </code>
        /// </summary>
        /// <param name="attributeType">元特性类型</param>
        protected Key(Type attributeType)
        {
            //Debug.Assert(typeof(Attribute).IsAssignableFrom(attributeType));
            if (!typeof(Attribute).IsAssignableFrom(attributeType))
                throw new ArgumentException("the attributeType: " + attributeType + " must be Attribute");
            this.attributeStrategy = StrategyFor(attributeType);
            this.typeLiteral = typeof(T);
            this.hashCode = ComputeHashCode();
        }

        /// <summary>
        /// 传入一个元特性对象构造：
        /// <example>
        /// new Key<Foo>(new Bar());
        /// </example>
        /// </summary>
        protected Key(Attribute attribute)
        {
            // no usages, not test-covered
            this.attributeStrategy = StrategyFor(attribute);
            this.typeLiteral = typeof(T);
            this.hashCode = ComputeHashCode();
        }

        /// <summary>
        /// 不适用任何参数构造：(主要方便用于测试）
        /// <example>
        /// new Key<Foo>() {}
        /// </example>
        /// </summary>
        public Key()
        {
            this.attributeStrategy = NullAnnotationStrategy.INSTANCE;
            this.typeLiteral = typeof(T);
            this.hashCode = ComputeHashCode();
        }

        /// <summary>
        /// 此构造用于静态方法使用获得另一个Key
        /// </summary>
        protected Key(AttributeStrategy attributeStrategy)
        {
            this.attributeStrategy = attributeStrategy;
            this.typeLiteral = typeof(T);
            this.hashCode = ComputeHashCode();
        }

        private int ComputeHashCode()
        {
            return typeLiteral.GetHashCode() * 31 + attributeStrategy.GetHashCode();
        }

        //public void DebugHashCode()
        //{
        //    int code = typeLiteral.GetHashCode() * 31 + attributeStrategy.GetHashCode();
        //}

        public Type AttributeType
        {
            get
            {
                return this.attributeStrategy.AttributeType;
            }
        }

        public Attribute Attribute
        {
            get
            {
                return this.attributeStrategy.Attribute;
            }
        }

        public bool HasAttributeType
        {
            get
            {
                return this.attributeStrategy.AttributeType != null;
            }
        }

        internal string AttributeName
        {
            get
            {
                Attribute attribute = this.attributeStrategy.Attribute;
                if (attribute != null)
                {
                    return attribute.ToString();
                }
                return attributeStrategy.AttributeType.ToString();
            }
        }

        /// <summary>
        /// 获取此key的提供者的key
        /// </summary>
        Key<IProvider<T>> ProviderKey
        {
            get
            {
                return new Key<IProvider<T>>(this.attributeStrategy.AttributeType);
            }
        }

        public override bool Equals(object o)
        {
            if (o == this)
            {
                return true;
            }
            if (!(o is Key<T>))
            {
                return false;
            }
            Key<T> other = (Key<T>)o;
            return attributeStrategy.Equals(other.attributeStrategy) && typeLiteral.Equals(other.typeLiteral);
        }

        public override int GetHashCode()
        {
            return this.hashCode;
        }

        public override string ToString()
        {
            return "Key[type=" + typeLiteral + ", annotation=" + attributeStrategy + "]";
        }

        /// <summary>
        /// 此key是否具有元特性实例
        /// </summary>
        public bool HasAttributes
        {
            get
            {
                return attributeStrategy.HasAttributes;
            }
        }

        /// <summary>
        /// 获取没有元特性实例只具有元特性类型的Key
        /// </summary>
        public Key<T> WithoutAttributes
        {
            get
            {
                return new Key<T>(attributeStrategy.WithoutAttributes);
            }
        }

        internal static AttributeStrategy StrategyFor(Attribute attribute)
        {
            Preconditions.CheckNotNull(attribute, "attribute");
            Type attributeType = attribute.GetType();
            EnsureIsBindingAttribute(attributeType);
            if (Attributes.IsMarker(attributeType))
            {
                return new AttributeTypeStrategy(attributeType, attribute);
            }
            return new AttributeInstanceStrategy(attribute);
        }

        /// <summary>
        /// 获取指定元特性类型的策略
        /// </summary>
        internal static AttributeStrategy StrategyFor(Type attributeType)
        {
            Preconditions.CheckNotNull(attributeType, "attribute type");
            //Debug.Assert(typeof(Attribute).IsAssignableFrom(attributeType));
            if (!typeof(Attribute).IsAssignableFrom(attributeType))
                throw new ArgumentException("the attributeType: " + attributeType + " must be Attribute");
            EnsureIsBindingAttribute(attributeType);
            return new AttributeTypeStrategy(attributeType, null);
        }

        private static void EnsureIsBindingAttribute(Type attributeType)
        {
            Preconditions.CheckArgument(Attributes.IsBindingAttribute(attributeType),
                                                           "{0} is not a binding annotation. Please annotate it with [BindingAnnotation]",
                                                           attributeType.Name);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //静态Get方法用于生成key
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// 内部使用的获取T指定的注入类型和制定元特性策略的Key实例
        /// </summary>
        internal static Key<T> Get(AttributeStrategy attributeStrategy)
        {
            return new Key<T>(attributeStrategy);
        }

        /// <summary>
        /// 用于非泛型方法
        /// </summary>
        /// <param name="keyClass"></param>
        /// <returns></returns>
        public static Key GetForType(Type keyClass)
        {
            //return  typeof(Key<>).MakeGenericType(new Type[] { keyClass })
            //     .GetConstructor(new Type[] { typeof(AttributeStrategy) })
            //     .Invoke(new object[]{NullAnnotationStrategy.INSTANCE}) as Key;
            Type keyType = typeof(Key<>).MakeGenericType(new Type[] { keyClass });
            foreach (ConstructorInfo constructor in keyType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic))
            {
                if (constructor.GetParameters().Length == 1 && constructor.GetParameters()[0].ParameterType == typeof(AttributeStrategy))
                {
                    return constructor.Invoke(new object[] { NullAnnotationStrategy.INSTANCE }) as Key;
                }
            }
            throw new Exception("no constuctor for key creation");
        }

        /// <summary>
        /// 获取T注入类型的Key
        /// </summary>
        public static Key<T> Get()
        {
            return new Key<T>(NullAnnotationStrategy.INSTANCE);
        }

        public static Key<T> Get(Type attributeType)
        {
            //Debug.Assert(typeof(Attribute).IsAssignableFrom(attributeType));
            if (!typeof(Attribute).IsAssignableFrom(attributeType))
                throw new ArgumentException("the attributeType:" + attributeType + " must be Attribute");
            return new Key<T>(attributeType);
        }

        public static Key<T> Get(Attribute attribute)
        {
            return new Key<T>(attribute);
        }

        public Key<X> OfType<X>()
        {
            return new Key<X>(attributeStrategy);
        }

    }

    internal class NullAnnotationStrategy : AttributeStrategy
    {
        //注意：不要将此类置于Key<>泛型类内部，否则这里的实例将不同！这是因为C#中泛型参数为一个确定的类型
        //将此类放置在泛型参数的Key中，即使此实例为静态只读，也会是不同的实例！
        public static readonly NullAnnotationStrategy INSTANCE = new NullAnnotationStrategy();

        public bool HasAttributes
        {
            get
            {
                return false;
            }
        }
        public AttributeStrategy WithoutAttributes
        {
            get
            {
                throw new NotSupportedException("Key already has no attributes.");
            }
        }

        public Attribute Attribute
        {
            get
            {
                return null;
            }
        }

        public Type AttributeType
        {
            get
            {
                return null;
            }
        }
        public override string ToString()
        {
            return "[none]";
        }
    }

    // this class not test-covered
    internal class AttributeInstanceStrategy : AttributeStrategy
    {
        readonly Attribute attribute;

        internal AttributeInstanceStrategy(Attribute attribute)
        {
            this.attribute = Preconditions.CheckNotNull(attribute, "attribute");
        }

        public bool HasAttributes
        {
            get
            {
                return true;
            }
        }

        public AttributeStrategy WithoutAttributes
        {
            get
            {
                return new AttributeTypeStrategy(AttributeType, attribute);
            }
        }

        public Attribute Attribute
        {
            get
            {
                return attribute;
            }
        }

        public Type AttributeType
        {
            get
            {
                return attribute.GetType();
            }
        }

        public override bool Equals(object o)
        {
            if (!(o is AttributeInstanceStrategy))
            {
                return false;
            }
            AttributeInstanceStrategy other = (AttributeInstanceStrategy)o;
            return attribute.Equals(other.Attribute);
        }

        public override int GetHashCode()
        {
            return attribute.GetHashCode();
        }

        public override string ToString()
        {
            return attribute.ToString();
        }
    }

    internal class AttributeTypeStrategy : AttributeStrategy
    {
        Type attributeType;
        Attribute attribute;

        public AttributeTypeStrategy(Type attributeType, Attribute attribute)
        {
            this.attributeType = Preconditions.CheckNotNull(attributeType, "attribute type");
            this.attribute = attribute;
        }

        public bool HasAttributes
        {
            get
            {
                return false;
            }
        }

        public AttributeStrategy WithoutAttributes
        {
            get
            {
                throw new NotSupportedException("Key already has no attributes.");
            }
        }

        public Attribute Attribute
        {
            get
            {
                return attribute;
            }
        }

        public Type AttributeType
        {
            get
            {
                return attributeType;
            }
        }

        public override bool Equals(object o)
        {
            if (!(o is AttributeTypeStrategy))
            {
                return false;
            }
            AttributeTypeStrategy other = (AttributeTypeStrategy)o;
            return AttributeType.Equals(other.AttributeType);
        }

        public override int GetHashCode()
        {
            return AttributeType.GetHashCode();
        }

        public override string ToString()
        {
            return "[" + AttributeType.Name + "]";
        }
    }
}
